 /*******************************************************************************
  * Copyright (c) 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  ******************************************************************************/

 package org.eclipse.ui.internal;

 import java.util.ArrayList ;
 import java.util.Iterator ;
 import java.util.List ;

 import org.eclipse.jface.action.ToolBarManager;
 import org.eclipse.jface.util.Geometry;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.DisposeEvent;
 import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.graphics.Cursor;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.ToolBar;
 import org.eclipse.swt.widgets.ToolItem;
 import org.eclipse.ui.IViewReference;
 import org.eclipse.ui.internal.dnd.AbstractDropTarget;
 import org.eclipse.ui.internal.dnd.DragUtil;
 import org.eclipse.ui.internal.dnd.IDragOverListener;
 import org.eclipse.ui.internal.dnd.IDropTarget;
 import org.eclipse.ui.presentations.PresentationUtil;

 /**
  * @since 3.3
  *
  */
 public class FastViewDnDHandler implements IDragOverListener {
     private String id;
     private ToolBarManager tbm;
     private WorkbenchWindow wbw;
     private ViewDropTarget dropTarget = null;

     private Listener dragListener = new Listener() {
         public void handleEvent(Event event) {
             Point position = DragUtil.getEventLoc(event);
             
             ToolBar toolbar = tbm.getControl();
             Point local = toolbar.toControl(position);
             ToolItem item = toolbar.getItem(local);
             IViewReference ref = (IViewReference) item
             .getData(ShowFastViewContribution.FAST_VIEW);

             if (ref != null) {
                 startDraggingFastView(ref, position, false);
             }
         }
     };

     class ViewDropTarget extends AbstractDropTarget {
         List panes;
         ToolItem curItem;

         /**
          * @param panesToDrop the list of ViewPanes to drop at the given position
          */
         public ViewDropTarget(List panesToDrop, ToolItem position) {
             setTarget(panesToDrop, position);
         }
         
         public void setTarget(List panesToDrop, ToolItem position) {
             panes = panesToDrop;
             this.curItem = position;
         }

         /* (non-Javadoc)
          * @see org.eclipse.ui.internal.dnd.IDropTarget#drop()
          */
         public void drop() {
             Perspective persp = wbw.getActiveWorkbenchPage().getActivePerspective();
             FastViewManager fvm = persp.getFastViewManager();

             int insertIndex = tbm.getControl().indexOf(curItem);
             Iterator iter = panes.iterator();
             while (iter.hasNext()) {
                 ViewPane pane = (ViewPane) iter.next();
                 IViewReference ref = pane.getViewReference();
                 adoptRef(ref);
                 fvm.addViewReference(id, insertIndex++, ref, !iter.hasNext());
             }
         }

         private void adoptRef(IViewReference ref) {
             Perspective persp = wbw.getActiveWorkbenchPage()
                     .getActivePerspective();
             PerspectiveHelper helper = persp.getPresentation();
             ContainerPlaceholder ourContainerPlaceholder = (ContainerPlaceholder) helper
                     .findPart(id, null);
             LayoutPart refPart = helper.findPart(ref.getId(), ref
                     .getSecondaryId());
             ILayoutContainer refContainer = refPart.container;
             if (refContainer != ourContainerPlaceholder) {
                 // remove the old part... if it's represented by a
 // placeholder then just remove it...
 if (refPart instanceof PartPlaceholder) {
                     if (refContainer instanceof ContainerPlaceholder) {
                         // just remove the placeholder
 ViewStack realContainer = (ViewStack) ((ContainerPlaceholder) refContainer)
                                 .getRealContainer();
                         realContainer.remove(refPart);
                     }
                     else if (refContainer instanceof ViewStack) {
                         refContainer.remove(refPart);
                     }
                 }
                 else {
                     // If its a real view ref then defref it...
 helper.derefPart(refPart);
                 }
                 PartPlaceholder newPlaceholder = new PartPlaceholder(ref
                         .getId());
                 ourContainerPlaceholder.add(newPlaceholder);
             }
         }
         
         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.internal.dnd.IDropTarget#getCursor()
          */
         public Cursor getCursor() {
             return DragCursors.getCursor(DragCursors.FASTVIEW);
         }

         public Rectangle getSnapRectangle() {
             if (curItem == null) {
                 // As long as the toolbar is not empty, highlight the place
 // where this view will appear (we
 // may have compressed it to save space when empty, so the actual
 // icon location may not be over the toolbar when it is empty)
 if (tbm.getControl().getItemCount() > 0) {
                     return getLocationOfNextIcon();
                 }
                 // If the toolbar is empty, highlight the entire toolbar
 return DragUtil.getDisplayBounds(tbm.getControl());
             }

             return Geometry.toDisplay(tbm.getControl(), curItem.getBounds());
         }
     }
     
     /**
      *
      */
     public FastViewDnDHandler(String id, final ToolBarManager tbm, WorkbenchWindow wbw) {
         this.id = id;
         this.tbm = tbm;
         this.wbw = wbw;
         
         // Hook the 'drop' listener to the control
 DragUtil.addDragTarget(tbm.getControl(), this);
         PresentationUtil.addDragListener(tbm.getControl(), dragListener);
         
         // Clean up on dispose
 tbm.getControl().addDisposeListener(new DisposeListener() {
             public void widgetDisposed(DisposeEvent e) {
                 DragUtil.removeDragTarget((Control)(e.widget), FastViewDnDHandler.this);
                 PresentationUtil.removeDragListener(tbm.getControl(), dragListener);
             }
         });
     }

     /**
      * Returns the toolbar item at the given position, in display coordinates
      * @param position
      */
     private ToolItem getToolItem(Point position) {
         ToolBar toolbar = tbm.getControl();
         Point local = toolbar.toControl(position);
         return toolbar.getItem(local);
     }
     
     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.dnd.IDragOverListener#drag(org.eclipse.swt.widgets.Control, java.lang.Object, org.eclipse.swt.graphics.Point, org.eclipse.swt.graphics.Rectangle)
      */
     public IDropTarget drag(Control currentControl, Object draggedObject,
             Point position, Rectangle dragRectangle) {
         // If we're trying to drop onto a 'standalone' stack, don't...
 if (isStandaloneStack())
             return null;
         
         ToolItem targetItem = getToolItem(position);
         if (draggedObject instanceof ViewPane) {
             ViewPane pane = (ViewPane) draggedObject;

             // Can't drag views between windows
 if (pane.getWorkbenchWindow() != wbw) {
                 return null;
             }

             List newList = new ArrayList (1);
             newList.add(draggedObject);

             return createDropTarget(newList, targetItem);
         }
         if (draggedObject instanceof ViewStack) {
             ViewStack folder = (ViewStack) draggedObject;

             if (folder.getWorkbenchWindow() != wbw) {
                 return null;
             }

             List viewList = new ArrayList (folder.getItemCount());
             LayoutPart[] children = folder.getChildren();

             for (int idx = 0; idx < children.length; idx++) {
                 if (!(children[idx] instanceof PartPlaceholder)) {
                     viewList.add(children[idx]);
                 }
             }

             return createDropTarget(viewList, targetItem);
         }

         return null;
     }
     /**
      * Tests the view references associated with the stack and
      * returns <code>true</code> if any view is a stand-alone view
      *
      * @return <code>true</code> is any view is stand-alone
      */
     private boolean isStandaloneStack() {
         Perspective persp = wbw.getActiveWorkbenchPage().getActivePerspective();
         List fvs = persp.getFastViewManager().getFastViews(id);
         for (Iterator iterator = fvs.iterator(); iterator.hasNext();) {
             IViewReference ref = (IViewReference) iterator.next();
             if (persp.isStandaloneView(ref))
                 return true;
         }
         
         return false;
     }

     private IDropTarget createDropTarget(List viewList, ToolItem targetItem) {
         if (dropTarget == null) {
             dropTarget = new ViewDropTarget(viewList, targetItem);
         } else {
             dropTarget.setTarget(viewList, targetItem);
         }
         return dropTarget;
     }

     /**
      * Returns the approximate location where the next fastview icon
      * will be drawn (display coordinates)
      */
     public Rectangle getLocationOfNextIcon() {
         ToolBar control = tbm.getControl();

         Rectangle result = control.getBounds();
         Point size = control.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
         result.height = size.y;
         result.width = size.x;
         
         boolean horizontal = (control.getStyle() & SWT.VERTICAL) == 0;
         if (control.getItemCount() == 0) {
             Geometry.setDimension(result, horizontal, 0);
         }
         
         int hoverSide = horizontal ? SWT.RIGHT : SWT.BOTTOM;

         result = Geometry.getExtrudedEdge(result, -Geometry.getDimension(
                 result, !horizontal), hoverSide);

         return Geometry.toDisplay(control.getParent(), result);
     }

     /**
      * Returns the index of the ToolItem fronting the view ref
      * @param toFind the view reference to find the index of
      * @return the index or -1 if not found
      */
     private int getIndex(IViewReference toFind) {
         ToolItem[] items = tbm.getControl().getItems();
         for (int i = 0; i < items.length; i++) {
             if (items[i].getData(ShowFastViewContribution.FAST_VIEW) == toFind) {
                 return i;
             }
         }

         return -1;
     }
     
     /**
      * Begins dragging a particular fast view
      *
      * @param ref
      * @param position
      */
     protected void startDraggingFastView(IViewReference ref, Point position,
             boolean usingKeyboard) {
         int index = getIndex(ref);
         if (index == -1)
             return;
         
         ToolItem item = tbm.getControl().getItem(index);
         Rectangle dragRect = Geometry.toDisplay(tbm.getControl(), item.getBounds());
         startDrag(((WorkbenchPartReference) ref).getPane(), dragRect, position,
                 usingKeyboard);
     }

     private void startDrag(Object toDrag, Rectangle dragRect, Point position,
             boolean usingKeyboard) {
         WorkbenchPage page = wbw.getActiveWorkbenchPage();
         Perspective persp = page.getActivePerspective();

         // Prevent dragging non-movable refs out of a minimized stack
 if (toDrag instanceof ViewPane) {
             ViewPane pane = (ViewPane) toDrag;
             if (!persp.isMoveable(pane.getViewReference()))
                 return;
         }
         
         IViewReference oldFastView = null;
         if (persp != null) {
             oldFastView = persp.getActiveFastView();

             if (page != null) {
                 page.hideFastView();
             }
         }

         if (page.isPageZoomed()) {
             page.zoomOut();
         }

         boolean success = DragUtil.performDrag(toDrag, dragRect, position,
                 !usingKeyboard);

         // If the drag was cancelled, reopen the old fast view
 if (!success && oldFastView != null && page != null) {
             page.toggleFastView(oldFastView);
         }
     }
 }

